/*******************************************************\
|* Author: Trevor Sundberg
\*******************************************************/

// Header protection
#pragma once
#ifndef SKUGO_WEAK_REFERENCE_HPP
#define SKUGO_WEAK_REFERENCE_HPP

// Includes
#include "SkugoDebug.hpp"
#include "SkugoTypes.hpp"

namespace Skugo
{
  // The base class of the weak reference
  class WeakBase
  {
  public:

    // Check if the weak pointer is valid
    bool IsValid();

    // Lock the node (returns true if the pointer is still valid)
    // Typically this is used in an if conditional
    bool Lock();

    // Unlock the node (it is an error to not match up a lock with an unlock)
    // Typically this is used at the end of an if statement afer a lock
    void Unlock();

    // Clear the pointer (and internally disconnect from the list)
    void Clear();

  protected:

    // Constructor (protected so that only derived classes can instantiate it)
    WeakBase(void* object);

    // Destructor
    ~WeakBase();

    // Disconnect from the list
    void Unlink();

    // Insert this object after a given link
    void InsertAfter(WeakBase* previous);

  protected:

    // A lock reference count
    sizeType mLockCount;

    // The next link in the list
    WeakBase* mNext;

    // The previous link in the list
    WeakBase* mPrev;

    // The object we're pointing at
    void* mObject;
  };

  // Put this inside the class declaration of an object
  // to make it so that weak pointers can point at it
  #define SkugoDeclareWeak() Skugo::LifetimeTracker WeakTracker;

  // A LifetimeTracker is aggrigated in a class and
  // allows weak pointers to point at that object
  class LifetimeTracker : public WeakBase
  {
  public:
    // Constructor
    LifetimeTracker();

    // Destructor
    ~LifetimeTracker();
  };

  // A weak pointer class so that we can point to an object without
  // worrying about accessing a bad pointer (at worst its NULL)
  template <typename T, LifetimeTracker T::* Tracker = &T::WeakTracker>
  class Weak : public WeakBase
  {
  public:
    // Constructor
    Weak(T* object) : WeakBase(object)
    {
      Initialize();
    }

    // Copy constructor
    Weak(const Weak& source) : WeakBase(source.mObject)
    {
      Initialize();
    }

    // Assignment operator
    Weak& operator =(const Weak& source)
    {
      // Protect against the stupid case of self-assignment
      if (&source != this)
      {
        // Clear what I currently am
        Clear();

        // Re-assign the object pointer
        mObject = source.mObject;

        // Re-add itself to the list
        Initialize();
      }

      // Return itself so it can be chained
      return *this;
    }

    // How we access the value pointed at by a weak reference
    T* operator ->() const
    {
      // Error checking
      ErrorIf(mObject == NULL, "Attempt to access a weak reference whose value is NULL!");

      // Return the object as a T*
      return static_cast<T*>(mObject);
    }

  private:

    // Initialize the object (called from multiple constructors)
    void Initialize()
    {
      // If our object is valid...
      if (mObject != NULL)
      {
        // Get the tracker object
        LifetimeTracker& tracker = static_cast<T*>(mObject)->*Tracker;

        // Insert this object after the tracker (which itself is actually a node)
        InsertAfter(&tracker);
      }
    }
  };
}

// End header protection
#endif
